package com.cskaoyan.javse.object._4hashcode;

/**
 * @description: hashCode()方法
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * hashCode()方法
 * 1.方法的声明:  public native int hashCode();
 *      它是一个本地方法,不需要任何参数,并且返回值是一个int整数
 *
 * 2.方法的作用:
 *      a.返回该对象的哈希码值。
 *      b.支持此方法是为了提高哈希表（例如 java.util.Hashtable 提供的哈希表）的性能。
 *          哈希表就可以简单看成key-value的结构,通过hashCode方法计算出哈希值,从而把对象作为哈希表的key,一旦哈希冲突过多,就会出现很多对象的key都是相同的情况,会严重影响哈希表的性能
 *
 * 在java当中hashCode方法可以看成是哈希映射的一个算法,也就是映射中的对应关系f,该方法的结果就是哈希映射中集合B中的元素
 * 哈希映射既然是映射就会出现"多对一"的情况,称之为"哈希冲突"
 *
 * 3.默认情况下的Object类当中的哈希值的计算:
 *      文档上的原话:
 *      实际上，由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。（这一般是通过将该对象的内部地址转换成一个int整数来实现的，但是 JavaTM 编程语言不需要这种实现技巧。）
 *
 *  3.如果对默认实现不满意,可以考虑自己重写hashCode方法,但是它的重写很重要,不能乱写,它也有自己的重写要求:
 *       a.在 Java 应用程序执行期间，在对同一对象多次调用 hashCode 方法时，必须一致地返回相同的整数，前提是将对象进行 equals比较时所用的信息没有被修改。(hashCode方法和equals方法息息相关,要重写都一起重写)
 *        从某一应用程序的一次执行到同一应用程序的另一次执行，该整数无需保持一致。
 *       b.如果根据 equals(Object)方法，两个对象是相等的，那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。
 *       hashCode方法和equals方法中,equals方法是"老大",equals方法认为对象相等时,这两个对象就是映射中A集合的同一个元素,因为映射不允许一对多,那么它们的哈希值必须完全一致
 *
 *      c.如果根据 equals(java.lang.Object) 方法，两个对象不相等，那么对这两个对象中的任一对象上调用hashCode方法不 要求一定生成不同的整数结果。(允许多对一,允许出现"哈希冲突")
 *      但是，程序员应该意识到，为不相等的对象生成不同整数结果可以提高哈希表的性能。
 *      (要求我们设计出更好,更精秒的哈希算法,从而尽量避免哈希冲突)
 *
 * 综上,在重写equals方法和hashCode方法时,只要它们用于比较和计算的内容是一致的,就能够满足上述要求
 * 比如equals方法如果比较对象中age,name的属性值,那么hashCode方法计算也应该用这两个属性值
 * 当然比较的内容越复杂,越不容易出现哈希冲突
 *
 * 最后注意两点:
 *      1.正常情况下,不要自己设计哈希算法,直接用idea自动生成就行了.如果有特殊需求,另行设计
 *      2.一旦重写hashCode方法后,默认的toString方法也不会再打印地址值了
 *          @ 后面跟的是一个十六进制的哈希值,不再是默认的地址值了
 *      3.如果类中有引用数据类型成员变量,那么就继续调用它的hashCode方法,也就需要重写它的hashCode方法
 *
 */
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student(19, 100, "张三");
        Student s2 = new Student(19, 100, "张三");
        System.out.println(s1);
        System.out.println(s2);

        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

        Student s3 = new Student(18, 100, "张三");
        System.out.println(s3.hashCode());
    }
}

class Student{
    int age;
    double score;
    String name;

    public Student() {
    }

    public Student(int age, double score, String name) {
        this.age = age;
        this.score = score;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        if (age != student.age) return false;
        if (Double.compare(student.score, score) != 0) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
}
